#include "seekelf.h"

// 创建默认头文件
Elf64_Ehdr elf64_header; 

// section_header_table
Elf64_Shdr *section_header_table = NULL;
size_t section_header_nums = 0;
 
// symbol table
Elf64_Sym *symbol_table = NULL; 
size_t symbol_nums = 0;

// relocation plt
Elf64_Rela *rela_plt_table = NULL;
size_t rela_plt_nums = 0;


void elf64_header_print();
void init_section_header_table(size_t nums);
void init_symbol_table(size_t nums);
void init_rela_plt_table(size_t nums);
void free_rela_plt_table();
void free_symbol_table();
void free_section_header_table();
Elf64_Shdr * get_sh_entry(char *name, char *shstrtab_ptr);

int main(int argc, char const *argv[])
{
    const char *file_path = argv[1];
    FILE *file = fopen(file_path, "rb");    

    // 读elf的头到elf64_header
    size_t bytes_read = fread(&elf64_header, sizeof(char), sizeof(Elf64_Ehdr), file);

    // elf64_header_print();

    // 读elf的段头表到section_header_table
    init_section_header_table(elf64_header.e_shnum);
    fseek(file, elf64_header.e_shoff, SEEK_SET);
    bytes_read = fread(section_header_table, sizeof(Elf64_Shdr), section_header_nums, file);

    // 读取elf的段头字符串表shstrtab，取用&shstrtab[index]，read .shstrtab
    Elf64_Shdr shstrtab_entry = section_header_table[elf64_header.e_shstrndx];
    fseek(file, shstrtab_entry.sh_offset, SEEK_SET);
    char shstrtab[shstrtab_entry.sh_size];
    bytes_read = fread(shstrtab, sizeof(char), shstrtab_entry.sh_size, file);
    
    // 读取elf的字符串表到strtab，取用&strtab[index]，read .strtab
    Elf64_Shdr * strtab_entry_ptr = get_sh_entry(".strtab", shstrtab);
    fseek(file, strtab_entry_ptr->sh_offset, SEEK_SET);
    char strtab[strtab_entry_ptr->sh_size];
    bytes_read = fread(strtab, sizeof(char), strtab_entry_ptr->sh_size, file);

    // 读取elf的符号表，read .symtab
    Elf64_Shdr * symtab_entry_ptr = get_sh_entry(".symtab", shstrtab);
    init_symbol_table(symtab_entry_ptr->sh_size/symtab_entry_ptr->sh_entsize);
    fseek(file, symtab_entry_ptr->sh_offset, SEEK_SET);
    bytes_read = fread(symbol_table, sizeof(Elf64_Sym), symbol_nums, file);

    // 读取.rela.plt
    Elf64_Shdr * rela_plt_ptr = get_sh_entry(".rela.plt", shstrtab);
    init_rela_plt_table(rela_plt_ptr->sh_size/rela_plt_ptr->sh_entsize);
    fseek(file, rela_plt_ptr->sh_offset, SEEK_SET);
    bytes_read = fread(rela_plt_table, sizeof(Elf64_Rela), rela_plt_nums, file);


    // 

    free_symbol_table();
    free_section_header_table();
    fclose(file);
    return 0;
}

void can_not_recognition();

void elf64_header_print()
{
    printf("ELF Header:\n");
    printf("  Magic:        ");
    for (size_t i = 0; i < EI_NIDENT; i++)
    {
        printf(" %x", elf64_header.e_ident[i]);
    }
    printf("\n");

    printf("  Class:         ");
    switch (elf64_header.e_ident[EI_CLASS])
    {
    case ELFCLASSNONE:
        printf("nvalid class\n");
        break;
    case ELFCLASS32:
        printf("32bits ELF\n");
        break;
    case ELFCLASS64:
        printf("64bits ELF\n");
        break;
    default:
        can_not_recognition();
        break;
    }

    printf("  Data:          ");
    switch (elf64_header.e_ident[EI_DATA])
    {
    case ELFDATANONE:
        printf("Invalid data encoding\n");
        break;
    case ELFDATA2LSB:
        printf("2's complement, little endian\n");
        break;
    case ELFDATA2MSB:
        printf("2's complement, big endian\n");
        break;
    default:
        can_not_recognition();
        break;
    }

    printf("  Version:       ");
    switch (elf64_header.e_ident[EI_VERSION])
    {
    case EV_CURRENT:
        printf("Current version 0x%x\n", EV_CURRENT);
        break;
    default:
        can_not_recognition();
        break;
    }

    printf("  OS/ABI:        ");
    switch (elf64_header.e_ident[EI_OSABI])
    {
    case ELFOSABI_SYSV:
        printf("UNIX System V ABI\n");
        break;
    default:
        can_not_recognition();
        break;
    } 

    printf("  ABI Version:   %d\n", elf64_header.e_ident[EI_ABIVERSION]);
    printf("  PAD index:     %d\n", EI_PAD);

    // type
    printf("  Type:          ");
    switch (elf64_header.e_type)
    {
    case ET_NONE:
        printf("No file type\n");
        break;
    case ET_REL:
        printf("Relocatable file\n");
        break;
    case ET_EXEC:
        printf("Executable file\n");
        break;
    case ET_DYN:
        printf("Shared object file\n");
        break;
    default:
        can_not_recognition();
        break;
    }

    // machine
    printf("  Machine:       ");
    switch (elf64_header.e_machine)
    {
    case EM_386:
        printf("Intel 80386\n");
        break;
    case EM_X86_64:
        printf("AMD x86-64 architecture\n");
        break;
    default:
        can_not_recognition();
        break;
    }

    //version
    printf("  Version:       ");
    switch (elf64_header.e_version)
    {
    case EV_CURRENT:
        printf("Current version 0x%x\n", EV_CURRENT);
        break;
    default:
        can_not_recognition();
        break;
    }

    printf("  Entry point address:  0x%x\n", elf64_header.e_entry);
    printf("  Start of program header table:  %d bytes\n", elf64_header.e_phoff);
    printf("  Start of section header table:  %d bytes\n", elf64_header.e_shoff);
    printf("  Flags:  %x\n", elf64_header.e_flags);
    printf("  Size of this header:  %d bytes\n", elf64_header.e_ehsize);    
    printf("  Size of entry in program header table:  %d bytes\n", elf64_header.e_phentsize);
    printf("  Number of program headers: %d\n", elf64_header.e_phnum);
    printf("  Size of entry in section header table: %d bytes\n", elf64_header.e_shentsize);
    printf("  Number of section headers: %d\n", elf64_header.e_shnum);
    printf("  Section header string table index: %d\n", elf64_header.e_shstrndx); 
    
}

inline void can_not_recognition()
{
    printf("can't recognize argument\n");
}

void init_section_header_table(size_t nums)
{
    section_header_table = malloc(nums * sizeof(Elf64_Shdr));
    if (section_header_table)
    {
        section_header_nums = nums;
        memset(section_header_table, 0, nums * sizeof(Elf64_Shdr));
    }
}

void init_symbol_table(size_t nums)
{
    symbol_table = malloc(nums * sizeof(Elf64_Sym));
    if (symbol_table != NULL)
    {
        symbol_nums = nums;
        memset(symbol_table, sizeof(Elf64_Sym), nums);
    }
}

void init_rela_plt_table(size_t nums)
{
    rela_plt_table = malloc(nums * sizeof(Elf64_Rela));
    if (rela_plt_table)
    {
        rela_plt_nums = nums;
        memset(rela_plt_table, sizeof(Elf64_Rela), nums);
    }
}

void free_rela_plt_table()
{
    free(rela_plt_table);
    rela_plt_table = NULL;
    rela_plt_nums = 0;
}

void free_symbol_table()
{
    free(symbol_table);
    symbol_table = NULL;
    symbol_nums = 0;
}

void free_section_header_table()
{
    free(section_header_table);
    section_header_table = NULL;
    section_header_nums = 0;
}

Elf64_Shdr * get_sh_entry(char *name, char *shstrtab_ptr)
{
    for (size_t i = 0; i < section_header_nums; i++)
    {
        if (!strcmp(name, shstrtab_ptr+section_header_table[i].sh_name))
        {
            return &section_header_table[i];
        }
    }
    return NULL;
}